<?php

/**
 *		//模板编译类//
 *		//这是一份试验品，地宝网5毛管理系统，由大街编写（zhongmingzi@gmail.com）
 *		//本程序默认环境：Zend Optimizer Yes / 3.3.3
 *		//		Apache/2.0.63 (Win32) PHP/5.2.12
 *		//		MySQL 支持 Yes / client lib version 5.0.89
 *		//		GD library Yes / bundled (2.0.34 compatible)
 *		//		eAccelerator/Memcache/xcache Yes
 *		//		cURL support enabled
 *		//时间：2011-01-10//
 */


if(!defined('IN')) {
	exit('Access Denied');
}

class template {

	var $templates_dir = './template/default/'; 			//模板路径;
	var $templates_cache = './data/template/'; 		//缓存模板路径;
	var $templates_default = './template/default/'; 		//默认模板路径;
	var $templates_new = false; 							//设置当次更新;
	var $templates_time = 360;				//过期时间,分钟计算;
	//以下的默认设置
	var $templates_charset = 'UTF-8';
	var $templates_postfix = '.html'; 		//模板后缀;
	var $templates_auto = true;				//自动更新模板;
	var $templates_caching = '.php';			//缓存后缀;
	//var $templates_debug = array();			//错误信息;
	var $templates_lonlabel = false;			//长标签;
	var $templates_space = true;			//清除无意义字符
	var $templates_php = false;				//为每个缓存头部增加PHP码;
	
	var $csscurmodules = '';					//当前的模块以便载入对应的CSS缓存
	var $jscurmodules = '';					//当前的模块以便载入对应的JS缓存
	//结果集;
	var $templates_file = array();					//模板文件
	var $templates_cache_file = null;				//缓存文件
	var $templates_xname = null;						//单个模板标识名
	var $templates_sname = null;						//单个子模板标识名
	var $templates_message = null;					//html内容;
	var $templates_update = 0;						//更新次数
	var $templates_assign = array();					//用户用smarty模式;
	var $PHPnew = 'PHPnew 7.0 Professional Fixed by DaJie';
	//缓存控制;
	var $templates_menu = array();					//用于记录每个模板信息;
	var $templates_menu_new = array();					//用于记录每个模板新信息;
	var $templates_replace = array();				//用于智能更新,可以将模板中的内容直接替换;
	//构造函数
	function __construct($cache_dir = null){
		//为属性作初始化，同时对name赋值
		$this->templates_auto = (boolean) $this->templates_auto;
		$this->templates_lonlabel = (boolean) $this->templates_lonlabel;
		$this->templates_space = (boolean) $this->templates_space;
		$this->templates_new = (boolean) $this->templates_new;
		$this->templates_cache = empty($cache_dir)?$this->templates_cache:$cache_dir;
		//默认模板目录不存在
		if(is_dir($this->templates_default) === false)
			$this->error('template_directory_notfound');
		//为控制设置流程,先引入配置文件， 里面记录着所有缓存文件的时间。
		if(is_file($this->templates_cache.'templates_caceh.php') && $this->templates_auto===true)
			$this->templates_menu = $this->templates_menu_new = include($this->templates_cache.'templates_caceh.php');
		//如果设置了当次更新，就将配置数组清空.//////这里原作者放到display方法了，会导致错误。
		if($this->templates_new==true){
		//exit('当次更新配置错误！');//结论正常
			$this->templates_menu = array();
		}
	}

	//解析开始;
	function display($file_name,$on = false){
		//这里是单个模板文件名
		$this->templates_xname = $file_name;
		//取得原始模板地址;
		$this->templates_file[$file_name] = $this->get_path($file_name);
		//取得缓存地址;
		$this->templates_cache_file[$file_name] = $this->get_path();
		//判断一下文件是否存在。 是否已经过期，然后生成并且显示。如果为true则载入缓存，否则重新生成。
		if($this->check($this->templates_xname)===true){
			return $this->view($this->templates_cache_file[$file_name]);
		}
		$this->templates_message = null;
		$this->templates_message = $this->replace_html(file_get_contents($this->templates_file[$file_name],LOCK_EX));
		//开始生成;这里得做一个判断，决定返回的是内容还是文件。
		return	$this->view($this->fileplus());
	}

	private function view($path_php){
		//这里得引入一个G，模板里的变量得用这个global里的G
		global $_G;
		static $str_array = null;
		// 多次view时， 以下内容仅进入一次。这样的结果后来添加到全局的变量失效。
		if($str_array === null){
			if(empty($this->templates_assign)===true){
				unset($GLOBALS['_ENV'],$GLOBALS['HTTP_ENV_VARS'],$GLOBALS['HTTP_SERVER_VARS'],
				$GLOBALS['HTTP_POST_VARS'],$GLOBALS['HTTP_GET_VARS'],$GLOBALS['HTTP_COOKIE_VARS'],
				$GLOBALS['HTTP_POST_FILES'],$GLOBALS['HTTP_COOKIE_VARS']);
				$phpnew_key = $phpnew_value = $phpnew_on = false;
				foreach ($GLOBALS as $phpnew_key =>$phpnew_value){
					//移除不需要的
					if($phpnew_key == 'GLOBALS') continue;
					$this->assign($phpnew_key,$phpnew_value);
					$phpnew_key = $phpnew_value = $phpnew_on = null;
				}
			}
			$str_array = $this->templates_assign;
		}
		//释放数组内容。
		if(empty($str_array) === false)
			extract($str_array,EXTR_SKIP);//这里也得修改，跳过去
		//因为在这里是封闭的，原本不存在任何变量包括G，$_G不是global $_G,是被赋值后，新的固定的$_G和那个全局的$_G不同。
		//所以这里的G必须为那个全局的G。而那个函数里的G就是global里的G。
		if(is_file($path_php) === true){
			//print_r($GLOBALS['_G']);exit;
			include_once($path_php);
		}
		return true;
	}
	
	//生成缓存文件
	private function fileplus(){
		file_put_contents($this->templates_cache_file[$this->templates_xname],$this->templates_message,LOCK_EX);
		$this->templates_message = array();
		return $this->templates_cache_file[$this->templates_xname];
	}

	//控制配置
	function __destruct(){
		if(is_array($this->templates_menu_new)===false)
			$this->templates_menu_new = array();
		foreach($this->templates_menu_new as $k => $v){
			if(isset($this->templates_menu[$k]) && $this->templates_menu[$k] == $v) continue;
			else {
				file_put_contents($this->templates_cache.'templates_caceh.php', sprintf("<? if(!defined('IN')) exit('Access Denied');\r\n return %s;",var_export($this->templates_menu_new,true)),LOCK_EX);
				break;
			}
		}
	}

	//控制sub配置，这里因为只在主模板重新生成的时候才生成，所以直接记录时间。
	private function templatessub(){
		if(is_array($this->templates_menu_new)===false)
			$this->templates_menu_new = array();
		if(is_file($this->templates_file[$this->templates_sname]) == true){
			$this->templates_menu_new[$this->templates_sname] = filemtime($this->templates_file[$this->templates_sname]);
			$this->templates_menu_new[$this->templates_xname.'%sub'][] = $this->templates_sname;
			$this->templates_menu_new[$this->templates_xname.'%sub'] = array_unique($this->templates_menu_new[$this->templates_xname.'%sub']);
		}
	}

	//路径处理
	private function get_path($file_name=null){
		//为默认路径做完整路径抓取;
		$this->path();
		if($file_name!=null){
			$file_all = $this->templates_dir.$file_name.$this->templates_postfix;//这里filename可以是common/header这类
			if($this->templates_dir===null || is_file($file_all)===false){
				$file_all = realpath($this->templates_default).DIRECTORY_SEPARATOR.$file_name.$this->templates_postfix;
			}
			if(is_file($file_all)===false){
				$this->error('template_notfound', $file_all);//模板文件找不到
			}
			return (string) $file_all;//返回的是当前模板的原始模板路径
		}else {
			$file = $this->templates_file[$this->templates_xname];
			$file_arr = pathinfo($file);
			$postfix = basename(dirname($file));
			return $this->templates_cache.$postfix.'_'.$file_arr['filename'].'_cache'.$this->templates_caching;//返回当前缓存文件路径
		}
		return false;
	}
	// 路径规范处理;
	private function path(){
		$this->templates_dir = realpath(strtr($this->templates_dir,array('./'=>'','//'=>'/','\\\\'=>'\\'))).DIRECTORY_SEPARATOR;
		$this->templates_cache = realpath(strtr($this->templates_cache,array('./'=>'','//'=>'/','\\\\'=>'\\'))).DIRECTORY_SEPARATOR;
		// 不能判断目录是否存在， 因为还有默认目录功能。
		if(strlen($this->templates_dir)<=2)
			$this->error('template_directory_notfound');
		if(strlen($this->templates_cache)<=2)
			$this->error('template_cache_directory_notfound');
	}
	//控制判断
	private function check($name){

		if(is_dir($this->templates_dir)===false){
			$this->error('template_directory_notfound');
		}

		if(is_dir($this->templates_cache)===false){
			$this->error('template_cache_directory_notfound');
		}
		//对检测的模板修改时间进行判断
		if(is_file($this->templates_file[$name]) == true){
			$is_menu_empty = empty($this->templates_menu[$name])?true:false;
			if($is_menu_empty){
				$this->templates_menu_new[$name] = filemtime($this->templates_file[$name]);
				return false;
			}
		}

		if(is_file($this->templates_cache_file[$name])===false || $this->templates_new===true){
			return false;
		}
		//如果自动更新和强制更新均关闭，则返回缓存文件
		if($this->templates_auto===false){
			return true;
		}
		//这里由fileatime改成filemtime，改为检测缓存模板的修改时间，这里进行自动更新判断。
		if(TIMESTAMP - filemtime($this->templates_cache_file[$name])>=$this->templates_time*60){
			return false;
		}
		
		if(isset($this->templates_menu[$name])===true && filemtime($this->templates_file[$name])!=$this->templates_menu[$name]){
			$this->templates_menu_new[$name] = filemtime($this->templates_file[$name]);
		//exit('check返回为真！');//结论正常
			return false;
		}
		//在这里要增加对这个模板的sub模块的检测,能够检测到这里，这个数组里的模块肯定多少都没变化，只是修改时间可能不同。
		$template_sub_arr = array();
		if(isset($this->templates_menu[$name.'%sub'])){
			$template_sub_arr = $this->templates_menu[$name.'%sub'];
			foreach($template_sub_arr as $k => $v){
				if(filemtime($this->get_path($v)) != $this->templates_menu[$v])
					return false;
			}
		}
		return true;
	}

	//假如用户用强制性;
	function assign($phpnew_var,$phpnew_value=null){
		$php_key = $php_val = null;
		if($phpnew_var=='')
		return false;
		if(is_array($phpnew_var)===true){
			foreach ($phpnew_var as $php_key => $php_val){
				if(is_array($php_val)===true){
					$this->assign($php_val);
				}
				if(is_numeric($php_key)===false || $php_key{0}!='_')
				$this->templates_assign[$php_key] = $php_val;
			}
		} else {
			if(is_numeric($phpnew_var)===false || $phpnew_var{0}!='_')
			$this->templates_assign[$phpnew_var] = $phpnew_value;
		}
	}

	//替换函数;
	protected function replace_html($template){
		//替换,那个被替换的方法在赋值的时候就再次运行了
		$template = preg_replace(
		"/[\n\r\t]*\{templatesub\s+(.+)\}[\n\r\t]*/iseU",
		"\$this->get_contents('$1')",
		$template
		);
		//直接输出php
		$template = preg_replace('/[\n\r\t]*\<\?php.*\\?\>[\n\r\t]*/iseU',
		'\'[php]\'.base64_encode(\'$0\').\'[/php]\'',
		$template
		);

		//静态替换
		$template = preg_replace(
		"/[\n\r\t]*\{html\s+(.+?)\}[\n\r\t]*/ise",
		"\$this->get_var('$1')",
		$template
		);
		//替换模板载入命令
		$template = preg_replace(
		"/[\n\r\t]*\{template\s+\\$(.+)\}[\n\r\t]*/isU",
		'<? include display(-%-$1); ?>',
		$template
		);
		$template = preg_replace(
		"/[\n\r\t]*\{template\s+(.+)\}[\n\r\t]*/isU",
		"<? include display('$1'); ?>",
		$template
		);
		//CSS判断语法
		$template = preg_replace(
		"/[\n\r\t]*\{csstemplate\}[\n\r\t]*/ies", "\$this->loadcsstemplate('\\1')", 
		$template
		);
		//JS判断语法
		$template = preg_replace(
		"/[\n\r\t]*\{jstemplate\}[\n\r\t]*/ies", "\$this->loadjstemplate('\\1')", 
		$template
		);

		//过滤 <!--{}-->
		$template = preg_replace("/\<\!\-\-\{(.+?)\}\-\-\>/s", "{\\1}", $template);
		//替换 PHP 换行符
		$template = str_replace("{LF}", "<?=\"\\n\"?>", $template);
		//替换直接变量输出
		$varRegexp = "((\\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)"
		. "(\[[a-zA-Z0-9_\-\.\"\'\[\]\$\x7f-\xff]+\])*)";
		$template = preg_replace("/\{(\\\$[a-zA-Z0-9_\[\]\'\"\$\.\x7f-\xff]+)\}/s", "<?=\\1?>", $template);
		$template = preg_replace("/$varRegexp/es", "\$this->addquote('<?=\\1?>')", $template);
		$template = preg_replace("/\<\?\=\<\?\=$varRegexp\?\>\?\>/es", "\$this->addquote('<?=\\1?>')", $template);
		//替换语言包变量
		$template = preg_replace("/\{lang\s+(.+?)\}/is", "<?=\$language['\\1']?>", $template);
		//替换特定函数
		$template = preg_replace(
		"/[\n\r\t]*\{eval\s+(.+?)\}[\n\r\t]*/ies",
		"\$this->stripvtags('<? \\1; ?>','')",
		$template
		);
		$template = preg_replace(
		"/[\n\r\t]*\{echo\s+(.+?)\}[\n\r\t]*/ies",
		"\$this->stripvtags('<? \\1; ?>','')",
		$template
		);
		$template = preg_replace(
		"/([\n\r\t]*)\{elseif\s+(.+?)\}([\n\r\t]*)/ies",
		"\$this->stripvtags('\\1<? } elseif(\\2) { ?>\\3','')",
		$template
		);
		$template = preg_replace(
		"/([\n\r\t]*)\{else\}([\n\r\t]*)/is",
		"\\1<? } else { ?>\\2",
		$template
		);
		//替换循环函数及条件判断语句
		for ($i = 0; $i < 6; $i++) {
			$template = preg_replace(
			"/[\n\r\t]*\{loop\s+(\S+)\s+(\S+)\}[\n\r]*(.+?)[\n\r]*\{\/loop\}[\n\r\t]*/ies",
			"\$this->stripvtags('<? if(is_array(\\1)) { foreach(\\1 as \\2) { ?>','\\3<? } } ?>')",
			$template
			);
			$template = preg_replace(
			"/[\n\r\t]*\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}[\n\r\t]*(.+?)[\n\r\t]*\{\/loop\}[\n\r\t]*/ies",
			"\$this->stripvtags('<? if(is_array(\\1)) { foreach(\\1 as \\2 => \\3) { ?>','\\4<? } } ?>')",
			$template
			);
			$template = preg_replace(
			"/([\n\r\t]*)\{if\s+(.+?)\}([\n\r]*)(.+?)([\n\r]*)\{\/if\}([\n\r\t]*)/ies",
			"\$this->stripvtags('\\1<? if(\\2) { ?>\\3','\\4\\5<? } ?>\\6')",
			$template
			);
		}
		//常量替换
		$template = preg_replace(
		"/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/s",
		"<?=\\1?>",
		$template
		);
		//删除 PHP 代码断间多余的空格及换行
		$template = preg_replace("/ \?\>[\n\r]*\<\? /s", " ", $template);

		//其他替换
		$template = preg_replace(
		"/\"(http)?[\w\.\/:]+\?[^\"]+?&[^\"]+?\"/e",
		"\$this->transamp('\\0')",
		$template
		);
		$template = preg_replace(
		"/\<script[^\>]*?src=\"(.+?)\".*?\>\s*\<\/script\>/ise",
		"\$this->stripscriptamp('\\1')",
		$template
		);
		$template = preg_replace(
		"/[\n\r\t]*\{block\s+([a-zA-Z0-9_]+)\}(.+?)\{\/block\}/ies",
		"\$this->stripblock('\\1', '\\2')",
		$template
		);
		$template = preg_replace('/[\n\r\t]*\[php\](.*)\[\/php\][\n\r\t]*/iseU','base64_decode(\'\\1\')',$template);
		$template = preg_replace('/<script.+script>/iseU','\'[script]\'.base64_encode(\'$0\').\'[/script]\'',$template);
		if ($this->templates_space===true) {
			$template = preg_replace(
			array('/\r\n/isU','/<<<EOF/isU'),array('',"\r\n<<<EOF\r\n"),$template);
		}
		$template = preg_replace('/[\n\r\t]*\[script\](.+)\[\/script\][\n\r\t]*/iseU','base64_decode(\'$1\')',$template);
		$template = strtr($template,array('-%-'=>'$','\"'=>'"','<style>'=>'<style type="text/css">','<script>'=>'<script type="text/javascript">'));
		$template = "<? if(!defined('IN')) exit('Access Denied'); ?>\n$template";
		if(empty($this->templates_php) === false)
		$template = $this->templates_php."\r\n".$template;

		$template = strtr($template,array('include display'=>'$this->display'));
		if($this->templates_lonlabel===true){
			$template = strtr($template,
			array('<?php'=>'<?','<?php echo'=>'<?='));
			$template = strtr($template,
			array('<?'=>'<?php ','<?='=>'<?php echo '));
		}
		$template = strtr($template,$this->templates_replace);
		$this->templates_update +=1;
		return (string) $template;
	}

	//模板引擎需要的函数. 开始.
	protected function get_contents($filename){
		$this->templates_file[$filename] = $this->get_path($filename);
		$this->templates_sname = $filename;
		if(is_file($this->templates_file[$filename]) === true){
			$files = file_get_contents($this->templates_file[$filename],LOCK_EX);
			//替换
			$files = preg_replace(
			"/[\n\r\t]*\{templatesub\s+(.+)\}[\n\r\t]*/iseU",
			"\$this->get_contents('$1')",
			$files
			);
			$this->templatessub();
			return $files;
		}
		return false;
	}
	
	protected function get_var($phpnew_name){
		if(isset($GLOBALS[$phpnew_name]))
		return $GLOBALS[$phpnew_name];
		if(isset($this->templates_assign[$phpnew_name]))
		return $this->templates_assign[$phpnew_name];

		return 'Static variable is not defined';
	}
	
	protected function transamp($template) {
		$template = str_replace('&', '&amp;', $template);
		$template = str_replace('&amp;amp;', '&amp;', $template);
		$template = str_replace('\"', '"', $template);
		return $template;
	}
	
	protected function stripvtags($expr, $statement) {
		$expr = str_replace("\\\"", "\"", preg_replace("/\<\?\=(\\\$.+?)\?\>/s", "\\1", $expr));
		$statement = str_replace("\\\"", "\"", $statement);
		return $expr . $statement;
	}

	protected function addquote($var) {
		return str_replace("\\\"", "\"", preg_replace("/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/s", "['\\1']", $var));
	}

	protected function stripscriptamp($s) {
		$s = str_replace('&amp;', '&', $s);
		return "<script src=\"$s\" type=\"text/javascript\"></script>";
	}

	protected function stripblock($var, $s) {
		$s = str_replace('\\"', '"', $s);
		$s = preg_replace("/<\?=\\\$(.+?)\?>/", "{\$\\1}", $s);
		preg_match_all("/<\?=(.+?)\?>/e", $s, $constary);
		$constadd = '';
		$constary[1] = array_unique($constary[1]);
		foreach($constary[1] as $const) {
			$constadd .= '$__' . $const  .' = ' . $const . ';';
		}
		$s = preg_replace("/<\?=(.+?)\?>/", "{\$__\\1}", $s);
		$s = str_replace('?>', "\n\$$var .= <<<EOF\n", $s);
		$s = str_replace('<?', "\nEOF;\n", $s);
		return "<?php\n$constadd\$$var = <<<EOF" . $s . "\nEOF;\n?>";
	}

	function loadcsstemplate() {
		global $_G;
		$scriptcss = '<link rel="stylesheet" type="text/css" href="<?=URL?>template/default/common/style.css" /><link rel="stylesheet" type="text/css" href="<?=URL?>template/default/common/custom.css" />';
		$content = $this->csscurmodules = '';
		$content = @implode('', file(ROOT.'./data/cache/style_'.STYLEID.'_module.css'));
		$content = preg_replace("/\[(.+?)\](.*?)\[end\]/ies", "\$this->cssvtags('\\1','\\2')", $content);
		if($this->csscurmodules) {
			$this->csscurmodules = preg_replace(array('/\s*([,;:\{\}])\s*/', '/[\t\n\r]/', '/\/\*.+?\*\//'), array('\\1', '',''), $this->csscurmodules);
			if(@$fp = fopen(ROOT.'./data/cache/style_'.STYLEID.'_'.$_G['basescript'].'_'.CURMODULE.'.css', 'w')) {
				fwrite($fp, $this->csscurmodules);
				fclose($fp);
			} else {
				exit('Can not write to cache files, please check directory ./data/ and ./data/cache/ .');
			}
			$scriptcss .= '<link rel="stylesheet" type="text/css" href="data/cache/style_{STYLEID}_'.$_G['basescript'].'_'.CURMODULE.'.css?{VERHASH}" />';
		}
		return $scriptcss;
	}
	
	function cssvtags($param, $content) {
		global $_G;
		$modules = explode(',', $param);
		foreach($modules as $module) {
			$module .= '::'; //fix notice
			list($b, $m) = explode('::', $module);
			if($b && $b == $_G['basescript'] && (!$m || $m == CURMODULE)) {
				$this->csscurmodules .= $content;
				return;
			}
		}
		return;
	}
	
	function loadjstemplate() {
		global $_G;
		$scriptjs = '<script src="<?=URL?>static/js/swfobject.js"></script><script src="<?=URL?>static/js/jquery-1.4.2.min.js"></script><script src="<?=URL?>static/js/jquery.ui.core.min.js"></script><script src="<?=URL?>static/js/jquery.ui.widget.min.js"></script><script src="<?=URL?>static/js/jquery.ui.tabs.min.js"></script><script src="<?=URL?>static/js/jquery.tipTip.min.js"></script><script src="<?=URL?>static/js/jquery.superfish.min.js"></script><script src="<?=URL?>static/js/jquery.supersubs.min.js"></script><script src="<?=URL?>static/js/jquery.validate_pack.js"></script><script src="<?=URL?>static/js/jquery.nyroModal.pack.js"></script><script type="text/javascript" src="<?=URL?>static/js/jquery.tagInput.min.js"></script><script type="text/javascript" src="<?=URL?>static/js/jquery.wysiwyg.min.js"></script>';
		$content = $this->jscurmodules = '';
		
		//
		/*$scriptjs .= '<script src="<?=URL?>static/js/<?=CURSCRIPT?>.js"></script>';*/
		return $scriptjs;
	}
	//删除缓存;
	function cache_dele($path = null){
		if($path===null){
			$path = $this->templates_cache;
		}
		$file_arr = scandir($path);
		foreach ($file_arr as $val){
			if($val === '.' || $val === '..'){
				continue;
			}
			if(is_dir($path.$val)===true)
			$this->cache_dele($path.$val.'/');
			if(is_file($path.$val)===true && $val!=='index.html')
			unlink($path.$val);
		}
	}
	
	//在这里增加一个错误函数，调用主模块的差错类显示
	function error($message, $tplname) {
		require_once libfile('class/error');
		error::template_error($message, $tplname);
	}
}



?>
